//
// File:    bte_coll_ufs.C
// Authors: Octavian Procopiuc <tavi@cs.duke.edu>
//          (using some code by Rakesh Barve)
//
// BTE_collection_ufs implementation.
//

#include <versions.H>
VERSION(bte_coll_ufs_C, "$Id: bte_coll_ufs.C,v 1.6 2002/07/20 21:36:14 tavi Exp $");

// For enabling/disabling library logging and assertions.
#include "lib_config.H"

#include <bte_coll_ufs.H>

BTE_err BTE_collection_ufs::new_block_internals(bid_t bid, void* &place) {
  if ((place = new char[header_.block_size]) == NULL) {    
    LOG_FATAL_ID("new() failed to alloc space for a block from file.");
    return BTE_ERROR_MEMORY_ERROR;
  }

  in_memory_blocks_++;
  return BTE_ERROR_NO_ERROR;
}

BTE_err BTE_collection_ufs::new_block_getid_specific(bid_t& bid) {
    bid_t *lbn;
    BTE_err err;
    if (header_.used_blocks < header_.last_block - 1) {

      tp_assert(freeblock_stack_ != NULL, 
		"BTE_collection_ufs internal error: NULL stack pointer");

      size_t slen = freeblock_stack_->stream_len();
      tp_assert(slen > 0, "BTE_collection_ufs internal error: empty stack");
      if ((err = freeblock_stack_->pop(&lbn)) != BTE_ERROR_NO_ERROR)
	return err;
      bid = *lbn;

    } else {

      tp_assert(header_.last_block <= header_.total_blocks, 
		"BTE_collection_ufs internal error: last_block>total_blocks");

      if (header_.last_block == header_.total_blocks) {
	// Increase the capacity for storing blocks in the stream by
	// 16 (only by 1 the first time around to be gentle with very
	// small coll's).
	if (header_.total_blocks == 1)
	  header_.total_blocks += 2;
	else if (header_.total_blocks <= 161)
	  header_.total_blocks += 8;
	else
	  header_.total_blocks += 64;
#define USE_FTRUNCATE_FOR_UFS 1
#if     USE_FTRUNCATE_FOR_UFS
	if (::ftruncate(bcc_fd_, bid_to_file_offset(header_.total_blocks))) {
	  LOG_FATAL_ID("Failed to ftruncate() to the new end of file.");
	  LOG_FATAL_ID(strerror(errno));
	  return BTE_ERROR_OS_ERROR;
	}
#else
	off_t curr_off;
	char* tbuf = new char[header_.os_block_size];
	if ((curr_off = ::lseek(bcc_fd_, 0, SEEK_END)) == (off_t)-1) {
	  LOG_FATAL_ID("Failed to lseek() to the end of file.");
	  LOG_FATAL_ID(strerror(errno));
	  return BTE_ERROR_OS_ERROR;
	}
	while (curr_off < bid_to_file_offset(header_.total_blocks)) {
	  ::write(bcc_fd_, tbuf, header_.os_block_size);
	  curr_off += header_.os_block_size;
	}
	file_pointer = curr_off;
	delete [] tbuf;
#endif
      }
      bid = header_.last_block++;
    }
    return BTE_ERROR_NO_ERROR;
}


BTE_err BTE_collection_ufs::get_block_internals(bid_t bid, void * &place) {

  if ((place = new char[header_.block_size]) == NULL) {    
    LOG_FATAL_ID("new() failed to alloc space for a block from file.");
    return BTE_ERROR_MEMORY_ERROR;
  }

  if (file_pointer != bid_to_file_offset(bid)) {
    if (::lseek(bcc_fd_, bid_to_file_offset(bid), SEEK_SET) !=  
	bid_to_file_offset(bid)) {
      LOG_FATAL_ID("lseek failed in file.");
      return BTE_ERROR_IO_ERROR;
    }
  }

  if (::read(bcc_fd_, (char *) place, header_.block_size) != 
      header_.block_size) {
    LOG_FATAL_ID("Failed to read() from file.");
    return BTE_ERROR_IO_ERROR;
  }

  file_pointer = bid_to_file_offset(bid) + header_.block_size;

  in_memory_blocks_++;
  return BTE_ERROR_NO_ERROR;
}


BTE_err BTE_collection_ufs::put_block_internals(bid_t bid, void * place, char dirty) {
  
  if ((bid < 0) || (bid >= header_.last_block)) {
    LOG_FATAL_ID("Incorrect bid in placeholder.");
    return BTE_ERROR_INVALID_PLACEHOLDER;
  }
  
  if (place == NULL) {
    LOG_FATAL_ID("Null block ptr field in placeholder.");
    return BTE_ERROR_INVALID_PLACEHOLDER;
  }

  //  if (place->dirty) 

  if (!read_only_) {

    if (file_pointer != bid_to_file_offset(bid)) {
      if (::lseek(bcc_fd_, bid_to_file_offset(bid), SEEK_SET) 
	  !=  bid_to_file_offset(bid)) {
	LOG_FATAL_ID("Failed to lseek() in file.");
	return BTE_ERROR_IO_ERROR;
      }
    }

    if (::write(bcc_fd_, place, header_.block_size) != header_.block_size) {
      LOG_FATAL_ID("Failed to write() block to file.");
      return BTE_ERROR_IO_ERROR;
    }

    file_pointer = bid_to_file_offset(bid) + header_.block_size;
  }

  delete [] (char *) place;

  in_memory_blocks_--;
  return BTE_ERROR_NO_ERROR;
}

BTE_err BTE_collection_ufs::sync_block(bid_t bid, void* place, char dirty = 1) {

  if ((bid < 0) || (bid >= header_.last_block)) {
    LOG_FATAL_ID("Incorrect bid in placeholder.");
    return BTE_ERROR_INVALID_PLACEHOLDER;
  }
  
  if (place == NULL) {
    LOG_FATAL_ID("Null block pointer.");
    return BTE_ERROR_INVALID_PLACEHOLDER;
  }

  if (!read_only_) {
    
    if (::lseek(bcc_fd_, bid_to_file_offset(bid), SEEK_SET) 
	!=  bid_to_file_offset(bid)) {
      LOG_FATAL_ID("Failed to lseek() in file.");
      return BTE_ERROR_IO_ERROR;
    }
    
    if (::write(bcc_fd_, place, header_.block_size) != header_.block_size) {
      LOG_FATAL_ID("Failed to write() block to file.");
      return BTE_ERROR_IO_ERROR;
    }

    file_pointer = bid_to_file_offset(bid) + header_.block_size;
  }
 
  stats_.record(BLOCK_SYNC);
  gstats_.record(BLOCK_SYNC);
  return BTE_ERROR_NO_ERROR;
}
